package app

import (
	"chatgpt/helper"
	"chatgpt/helper/code"
	"chatgpt/logq"
	"chatgpt/model/app"
	"chatgpt/services/datasrv"
	dbapp "chatgpt/services/datasrv/app"
	"chatgpt/services/datasrv/sqlhelper"
	"context"
	"github.com/slclub/glog"
	"github.com/slclub/go-tips"
	"github.com/slclub/go-tips/array"
	"strings"
	"time"
)

type BenefitSrv struct {
	Benefit      BenefitOnly
	BenefitUser  BenefitUser
	BenefitScope BenefitScope
	Scope        ScopeSrv
	Cache        BenefitCache
}

func (self *BenefitSrv) CheckScope(uuid, profitType string, profit string) bool {
	benes := self.GetWithUUID(uuid)
	fpath := func(typ string, profit string, scope *app.Scope) bool { return false }
	switch profitType {
	case helper.BENFIT_SCOPE_PATH:
		fpath = func(typ string, profit2 string, scope *app.Scope) bool {
			if typ != scope.ScopeType {
				return false
			}
			if tips.StrPos(profit2, scope.ScopeValue) != -1 {
				return true
			}
			return false
		}
	}
	for _, obj := range benes {
		for _, scope := range obj.Scopes {
			b := fpath(profitType, profit, scope)
			if b {
				return true
			}
		}
	}
	return false
}

// 获取有效用户权益列表
func (self *BenefitSrv) GetWithUUID(uuid string) []*app.Benefit {
	return self.GetFromDB(uuid, false)
}

// 直接从数据库中获取列表
func (self *BenefitSrv) GetList() []*app.Benefit {
	datas := self.Benefit.GetList(nil)

	return datas
}

// 直接从数据库中获取列表
func (self *BenefitSrv) GetListWithIDS(ids []int) []*app.Benefit {
	if len(ids) == 0 {
		return []*app.Benefit{}
	}
	datas := self.Benefit.GetList(&sqlhelper.Where{
		Conds: []*sqlhelper.CondItem{
			sqlhelper.WhereCond("BenefitID", ids, "IN"),
		},
	})

	return datas
}

// 为某个用户附加 权益
func (self *BenefitSrv) CallAttachWithID(uuid string, benefit_id int, dur time.Duration) code.CODENO {
	//bene := self.FirstBenefit(benefit_id, uuid)

	// 根据权限设置时间
	bene := self.Benefit.FirstDB(&sqlhelper.Where{
		Conds: []*sqlhelper.CondItem{
			sqlhelper.WhereCond("BenefitID", benefit_id),
		},
		Formula: "{1}",
	})

	//newbene1 := updateBenefitTime(bene, &newbene)
	return self.AttachWithBenefit(uuid, bene, map[string]any{
		"Duration": int(dur),
	})
}

func (self *BenefitSrv) AttachWithBenefit(uuid string, bene *app.Benefit, data map[string]any) code.CODENO {
	// 重新计算 过期时间

	// 赋值
	bu := &app.BenefitUser{
		BenefitID:  bene.BenefitID,
		BenefitInt: tips.Int(data["Duration"]),
	}
	bu.UUID = uuid
	switch bene.Action {
	case app.BENEFIT_VIP:
		bu.BenefitInt *= app.EXPIRE_DAY
	}
	// 计算 权益时间
	self.BenefitUser.ExplainExpire(bu)
	return self.BenefitUser.Save(bu)
}

func (self *BenefitSrv) AttachWithCash(uuid string, bene *app.Benefit, data map[string]any) code.CODENO {
	cash := tips.Int(data["cash"])
	res := sqlhelper.ResWhere{}
	mc := datasrv.AppDB.UserSave(context.Background(), &sqlhelper.ReqWhere{
		Object: map[string]any{
			"CashTotal": cash,
			"CashLeft":  cash,
		},
		Where: &sqlhelper.Where{
			Conds: []*sqlhelper.CondItem{sqlhelper.WhereCond("UUID", uuid)},
		},
	}, &res)
	return mc
}

func (self *BenefitSrv) GetFromDB(uuid string, all bool) []*app.Benefit {
	bus := self.BenefitUser.GetListByUUID(uuid, all)
	if bus == nil || len(bus) == 0 {
		return nil
	}
	bids := []int{}
	array.DimensionPlugk(&bids, bus, "BenefitID")
	buss := self.Benefit.GetListByBenefitIDS(bids)
	scops := self.BenefitScope.GetScopeMapArray(bids)
	for _, obj := range buss {
		//for _, cf := range bus {
		//	if cf.BenefitID != obj.BenefitID {
		//		continue
		//	}
		//
		//}
		obj.Scopes = scops[obj.BenefitID]
	}
	return buss
}

func (self *BenefitSrv) FirstBenefit(benefit_id int, uuid string) *app.Benefit {
	obj := self.Benefit.FirstDB(&sqlhelper.Where{
		Conds: []*sqlhelper.CondItem{
			sqlhelper.WhereCond("BenefitID", benefit_id),
		},
		Formula: "{1}",
	})
	if obj == nil {
		return nil
	}
	//buo := self.BenefitUser.GetFirstDB(&sqlhelper.Where{
	//	Conds: []*sqlhelper.CondItem{
	//		sqlhelper.WhereCond("BenefitID", benefit_id),
	//		sqlhelper.WhereCond("UUID", uuid),
	//	},
	//	Formula: "{1} AND {2}",
	//})

	return obj
}

func (self *BenefitSrv) Save(bene *app.Benefit) code.CODENO {
	return self.Benefit.Save(bene)
}

// cache
type BenefitCache struct {
}

// benefit only
type BenefitOnly struct {
}

func (self *BenefitOnly) GetList(where *sqlhelper.Where) []*app.Benefit {
	return self.GetListDB(where)
}

func (self *BenefitOnly) GetListByBenefitIDS(ids []int) []*app.Benefit {
	return self.GetListDB(&sqlhelper.Where{
		Conds:   []*sqlhelper.CondItem{sqlhelper.WhereCond("BenefitID", ids, "IN")},
		Formula: "{1}",
	})
}

func (self *BenefitOnly) FirstDB(where *sqlhelper.Where) *app.Benefit {
	datapp := dbapp.AppApi{}
	res := sqlhelper.ResWhere{}
	mc := datapp.BenefitFirst(context.Background(), &sqlhelper.ReqWhere{
		Where: where,
	}, &res)
	if res.MsgCode != code.SUCCESS {
		logq.InfoF("BenefitOnly.GET code:%v", mc)
		return nil
	}
	data, ok := res.Data.(map[string]any)
	if !ok {
		return nil
	}
	bene := app.Benefit{}
	bene.ConvFromMap(data)
	return &bene
}

func (self *BenefitOnly) GetListDB(where *sqlhelper.Where) []*app.Benefit {
	datapp := dbapp.AppApi{}
	res := sqlhelper.ResWhere{}
	mc := datapp.BenefitSelect(context.Background(), &sqlhelper.ReqWhere{
		Where: where,
	}, &res)
	if res.MsgCode != code.SUCCESS {
		logq.InfoF("BenefitOnly.GET code:%v", mc)
		return nil
	}
	data_arr, ok := res.Data.([]any)
	if !ok {
		return nil
	}

	rtn := []*app.Benefit{}
	for _, v := range data_arr {
		m, ok := v.(map[string]any)
		if !ok {
			continue
		}
		tmp := &app.Benefit{}
		tmp.ConvFromMap(m)
		rtn = append(rtn, tmp)
	}
	return rtn
}

func (self *BenefitOnly) Save(bene *app.Benefit) code.CODENO {
	datapp := dbapp.AppApi{}
	res := sqlhelper.ResWhere{}
	mc := datapp.BenefitSave(context.Background(), &sqlhelper.ReqWhere{Object: bene.ConvToMap()}, &res)
	res.MsgCode = mc
	if mc == 0 {
		res.MsgCode = res.MsgCode
	}
	return code.SUCCESS
}

// benefit user
type BenefitUser struct {
}

func (self *BenefitUser) GetExpire(uuid string) int {
	list := self.GetListByUUID(uuid, false)
	if len(list) == 0 {
		return 0
	}
	expire := 0
	for _, v := range list {
		if v.ExpireAt == -1 {
			return v.ExpireAt
		}
		if v.ExpireAt > expire {
			expire = v.ExpireAt
		}
	}
	return expire
}

func (self *BenefitUser) GetListByUUID(uuid string, all bool) []*app.BenefitUser {
	where := (&sqlhelper.Where{
		Conds: []*sqlhelper.CondItem{
			sqlhelper.WhereCond("UUID", uuid),
			sqlhelper.WhereCond("ExpireAt", -1),
			sqlhelper.WhereCond("ExpireAt", time.Now().Unix(), ">="),
		},
		Formula: "{1} AND ( {2} OR {3})",
	})
	if all {
		where = (&sqlhelper.Where{
			Conds: []*sqlhelper.CondItem{
				sqlhelper.WhereCond("UUID", uuid),
			},
			Formula: "{1}",
		})
	}
	return self.GetListDB(where)
}

func (self *BenefitUser) GetListDB(where *sqlhelper.Where) []*app.BenefitUser {
	datapp := dbapp.AppApi{}
	res := sqlhelper.ResWhere{}
	mc := datapp.BenefitUserSelect(context.Background(), &sqlhelper.ReqWhere{
		Where: where,
	}, &res)
	if res.MsgCode != code.SUCCESS {
		logq.InfoF("BenefitUser.GET code:%v", mc)
		return nil
	}
	data_arr, ok := res.Data.([]any)
	if !ok {
		return nil
	}

	rtn := []*app.BenefitUser{}
	for _, v := range data_arr {
		m, ok := v.(map[string]any)
		if !ok {
			continue
		}
		tmp := &app.BenefitUser{}
		tmp.ConvFromMap(m)
		rtn = append(rtn, tmp)
	}
	return rtn
}

func (self *BenefitUser) GetFirstDB(where *sqlhelper.Where) *app.BenefitUser {
	datapp := dbapp.AppApi{}
	res := sqlhelper.ResWhere{}
	mc := datapp.BenefitUserFirst(context.Background(), &sqlhelper.ReqWhere{
		Where: where,
	}, &res)
	if res.MsgCode != code.SUCCESS {
		logq.InfoF("BenefitUser.GET code:%v", mc)
		return nil
	}
	data_arr, ok := res.Data.(map[string]any)
	if !ok {
		return nil
	}

	rtn := &app.BenefitUser{}
	rtn.ConvFromMap(data_arr)
	return rtn
}

func (self *BenefitUser) Save(bu *app.BenefitUser) code.CODENO {
	res := sqlhelper.ResWhere{}
	mc := datasrv.AppDB.BenefitUserSave(context.Background(), &sqlhelper.ReqWhere{
		Object: bu.ConvToMap(),
		Where: &sqlhelper.Where{
			Conds: []*sqlhelper.CondItem{
				sqlhelper.WhereCond("UUID", bu.UUID),
			},
			Formula: "{1}",
		},
	}, &res)
	if res.MsgCode != code.SUCCESS {
		logq.InfoF("BenefitUser.GET code:%v", mc)
		return res.MsgCode
	}
	return code.SUCCESS
}

func (self *BenefitUser) Delete(where *sqlhelper.Where) code.CODENO {
	res := sqlhelper.ResWhere{}
	mc := datasrv.AppDB.BenefitUserDelete(context.Background(), &sqlhelper.ReqWhere{
		Where: where,
	}, &res)
	if res.MsgCode != code.SUCCESS {
		logq.InfoF("BenefitUser.GET code:%v", mc)
		return res.MsgCode
	}
	return code.SUCCESS
}

func (self *BenefitUser) DeleteBy(benefit_id int) code.CODENO {
	return self.Delete(&sqlhelper.Where{
		Conds: []*sqlhelper.CondItem{
			sqlhelper.WhereCond("BenefitID", benefit_id),
		},
		Formula: "{1}",
	})
}

func (self *BenefitUser) ExplainExpire(bu *app.BenefitUser) code.CODENO {
	if strings.Trim(bu.UUID, " ") == "" || bu.BenefitInt == 0 {
		return code.DATA_NIL
	}
	buser := self.GetFirstDB(&sqlhelper.Where{
		Conds: []*sqlhelper.CondItem{
			sqlhelper.WhereCond("UUID", bu.UUID),
			sqlhelper.WhereCond("BenefitID", bu.BenefitID),
		},
		Formula: "{1} AND {2}",
	})
	if buser != nil && buser.ExpireAt == -1 {
		glog.Infof("BenefitSrv.AttachWithBenefit code:%v", int(code.NON_BENEFIT_HEIGH_ERR))
		return code.NON_BENEFIT_HEIGH_ERR
	}

	now := time.Now()
	duration := time.Duration(bu.BenefitInt) * time.Second

	if buser == nil {
		bu.ExpireAt = int(now.Add(duration).Unix())
	} else {
		bu.ID = buser.ID
		if buser.ExpireAt == -1 {
			bu.ExpireAt = -1
			return code.SUCCESS
		}
		expire := time.Unix(int64(buser.ExpireAt), 0)
		if expire.Sub(now) <= 0 {
			bu.ExpireAt = int(now.Add(duration).Unix())
		} else {
			bu.ExpireAt = int(expire.Add(duration).Unix())
		}
	}
	return code.SUCCESS
}

// BenefitScope
type BenefitScope struct {
}

func (self *BenefitScope) GetScopeArray(ids []int) []*app.Scope {
	scopes := []*app.Scope{}
	self.GetScopesWithBenefitIDS(ids, &scopes)
	return scopes
}

func (self *BenefitScope) GetScopeMapArray(ids []int) map[int][]*app.Scope {
	scopes := map[int][]*app.Scope{}
	self.GetScopesWithBenefitIDS(ids, &scopes)
	return scopes
}

func (self *BenefitScope) GetScopesWithBenefitIDS(ids []int, dest any) {
	datas := self.GetFromDB(&sqlhelper.Where{
		Conds: []*sqlhelper.CondItem{
			sqlhelper.WhereCond("BenefitID", ids, "IN"),
		},
		Formula: "{1}",
	})
	scope_ids := []int{}
	array.DimensionPlugk(&scope_ids, datas, "ScopeID")
	if len(scope_ids) == 0 {
		return
	}
	scopeSrv := ScopeSrv{}
	scopes := scopeSrv.GetFromDB(&sqlhelper.Where{
		Conds: []*sqlhelper.CondItem{
			sqlhelper.WhereCond("ScopeID", scope_ids, "IN"),
		},
	})
	if val, ok := dest.(*[]*app.Scope); ok {
		*val = scopes
		return
	}
	maparr, ok := dest.(*map[int][]*app.Scope)
	if !ok {
		return
	}
	newdatas := map[int][]*app.Scope{}
	for _, rf := range datas {
		obj, ok := (newdatas)[rf.BenefitID]
		if !ok {
			newdatas[rf.BenefitID] = []*app.Scope{}
			obj = newdatas[rf.BenefitID]
		}
		for _, v := range scopes {
			if v.ScopeID != rf.ScopeID {
				continue
			}
			obj = append(obj, v)
			break
		}
		newdatas[rf.BenefitID] = obj
	}
	*maparr = newdatas
}

func (self *BenefitScope) GetFromDB(where *sqlhelper.Where) []*app.BenefitScope {
	res := sqlhelper.ResWhere{}
	mc := datasrv.AppDB.BenefitScopeSelect(context.Background(), &sqlhelper.ReqWhere{
		Where: where,
	}, &res)
	if res.MsgCode != code.SUCCESS {
		logq.InfoF("BenefitScope.GET code:%v", mc)
		return nil
	}
	data_arr, ok := res.Data.([]any)
	if !ok {
		return nil
	}

	rtn := []*app.BenefitScope{}
	for _, v := range data_arr {
		m, ok := v.(map[string]any)
		if !ok {
			continue
		}
		tmp := &app.BenefitScope{}
		tmp.ConvFromMap(m)
		rtn = append(rtn, tmp)
	}
	return rtn
}

func (self *BenefitScope) Save(bs *app.BenefitScope) code.CODENO {
	res := sqlhelper.ResWhere{}
	mc := datasrv.AppDB.BenefitScopeSave(context.Background(), &sqlhelper.ReqWhere{
		Object: bs.ConvToMap(),
		Where: &sqlhelper.Where{
			Conds: []*sqlhelper.CondItem{
				sqlhelper.WhereCond("BenefitID", bs.BenefitID),
				sqlhelper.WhereCond("ScopeID", bs.ScopeID),
			},
			Formula: "{1} AND {2}",
		},
	}, &res)
	if res.MsgCode != code.SUCCESS {
		logq.InfoF("BenefitUser.Save code:%v", mc)
		return res.MsgCode
	}
	bs.ConvFromMap(res.Data.(map[string]any))
	return code.SUCCESS
}

func (self *BenefitScope) Delete(benefit_id, scope_id int) code.CODENO {
	res := sqlhelper.ResWhere{}
	mc := datasrv.AppDB.BenefitScopeDelete(context.Background(), &sqlhelper.ReqWhere{
		Where: &sqlhelper.Where{
			Conds: []*sqlhelper.CondItem{
				sqlhelper.WhereCond("BenefitID", benefit_id),
				sqlhelper.WhereCond("ScopeID", scope_id),
			},
			Formula: "{1} AND {2}",
		},
	}, &res)
	//res.MsgCode = mc
	if res.MsgCode != code.SUCCESS {
		logq.InfoF("BenefitUser.Save code:%v", mc)
		return res.MsgCode
	}
	return code.SUCCESS
}
